/**
 * Trabajo de Ingeniería de Software 2
 * Año 2008
 * Sistema de Reserva de Recursos.
 * Desarrolladores: - Marcelo Ferreira.
 *                  - Christian Gómez.
 *                  - Marcelo Rodas.
 */
package controller;

import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.model.SelectItem;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceUnit;
import javax.persistence.Query;
import javax.faces.application.FacesMessage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import javax.faces.FacesException;
import java.util.HashMap;
//import javax.faces.validator.ValidatorException;
import javax.servlet.http.HttpSession;
import javax.transaction.UserTransaction;
import modelo.Devolucion;
import modelo.Estadorecurso;
import modelo.Usoretiro;
import modelo.Usuario;

/**
 * Clase Controladora de la Devolucion de Reservas
 * Este Controlador vincula y procesa las funcionalidades del jsp 
 * relacionadas a la Devolución, con los datos correspondientes 
 * en la Base de Datos.
 * @author mrodas
 * @version 2.0
 */
public class DevolucionController implements DevolucionControllerInterface {

    private Devolucion devolucion = null;
    private List<Devolucion> devolucions = null;
    @Resource
    private UserTransaction utx = null;
    @PersistenceUnit(unitName = "sistemaReservaPU")
    private EntityManagerFactory emf = null;

    /**
     * Getter del Entity Manager desde su Factory
     * @return EntityManager
     */
    public EntityManager getEntityManager() {
        return emf.createEntityManager();
    }
    /*
    public int batchSize = 100;
    private int firstItem = 0;*/
    private int itemCount = -1;

    /**
     * Getter de las Devoluciones Disponibles en la base de datos
     * @return SelectItem[]
     */
    public SelectItem[] getDevolucionsAvailableSelectMany() {
        return getDevolucionsAvailable(false);
    }

    /**
     * Getter de las Devoluciones Disponibles en la base de datos con un
     * elemento vacio al principio del vector respuesta.
     * @return SelectItem[]
     */
    public SelectItem[] getDevolucionsAvailableSelectOne() {
        return getDevolucionsAvailable(true);
    }

    private SelectItem[] getDevolucionsAvailable(boolean one) {
        List<Devolucion> allDevolucions = getDevolucions(true);
        int size = one ? allDevolucions.size() + 1 : allDevolucions.size();
        SelectItem[] items = new SelectItem[size];
        int i = 0;
        if (one) {
            items[0] = new SelectItem("", "---");
            i++;
        }
        for (Devolucion x : allDevolucions) {
            items[i++] = new SelectItem(x, x.toString());
        }
        return items;
    }

    /**
     * Getter de la Devolucion representada.
     * @return Devolucion
     */
    public Devolucion getDevolucion() {
        if (devolucion == null) {
            devolucion = getDevolucionFromRequest();
        }
        if (devolucion == null) {
            devolucion = new Devolucion();
        }
        return devolucion;
    }

    /**
     * Función para asignar el atributo devolucions y se pueda
     * utilizar en la lista de Devoluciones.
     * @return String
     */
    public String listarSetup() {
        reset(true);
        devolucions = getDevolucions(false);
        return "devolucion_listar";
    }

    /**
     * 
     * @return
     */
    /* public String listSetup() {
    reset(true);
    devolucions= getDevolucions(false);
    return "devolucion_list";
    }*/
    /**
     * 
     * @return
     */
    /*public String createSetup() {
    reset(false);
    devolucion = new Devolucion();
    return "devolucion_create";
    }*/
    /**
     * Función para el Seteo de una posible Devolución
     * @return "devolucion_create"
     */
    public String crearSetup() {
        reset(false);
        devolucion = new Devolucion(this.getItemMax());
        Usoretiro iduso = (Usoretiro) this.getIdUsoFromRequest();
        devolucion.setIdUsoRetiro(iduso);
        devolucion.setFechaDevolucion(new Date());
        devolucion.setHoraDevolucion(new Date());
        return "devolucion_create";
    }

    /**
     * Función para la creación y persistencia de una devolución.
     * @return String
     */
    public String create() {
        EntityManager em = getEntityManager();
        try {
            utx.begin();
            Estadorecurso idEstadoRecurso = devolucion.getIdEstadoRecurso();
            if (idEstadoRecurso != null) {
                idEstadoRecurso = em.getReference(idEstadoRecurso.getClass(), idEstadoRecurso.getIdEstadoRecurso());
                devolucion.setIdEstadoRecurso(idEstadoRecurso);
            }
            Usoretiro idUsoRetiro = devolucion.getIdUsoRetiro();
            if (idUsoRetiro != null) {
                idUsoRetiro = em.getReference(idUsoRetiro.getClass(), idUsoRetiro.getIdUsoRetiro());
                devolucion.setIdUsoRetiro(idUsoRetiro);
            }
            em.persist(devolucion);
            if (idEstadoRecurso != null) {
                idEstadoRecurso.getDevolucionCollection().add(devolucion);
                idEstadoRecurso = em.merge(idEstadoRecurso);
            }
            if (idUsoRetiro != null) {
                idUsoRetiro.getDevolucionCollection().add(devolucion);
                idUsoRetiro = em.merge(idUsoRetiro);
            }
            //Recupera los atributos de la sesion
            HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            //Para la audioria, que dispara el trigger de auditoria
            Usuario usuarioLogeado = (Usuario) session.getAttribute("user");
            devolucion.setUserAudita(usuarioLogeado.getIdUsuario());

            utx.commit();
            addSuccessMessage("La Devolucion fue procesada Exitosamente.");
        } catch (Exception ex) {
            try {
                if (findDevolucion(devolucion.getIdDevolucion()) != null) {
                    addErrorMessage("La Devolucion " + devolucion + " ya existe.");
                } else {
                    ensureAddErrorMessage(ex, "Ocurrio un error de persistencia.");
                }
                utx.rollback();
            } catch (Exception e) {
                ensureAddErrorMessage(e, "Ocurrio un error tratando de hacer roll-back de la transacción.");
            }
            return null;
        } finally {
            em.close();
        }
        // Codigo para hacer un refresh de la lista de Reservas y que solo
        // muestre las que esten pendiente de USO/RETIRO
        try {
            HttpSession session = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
            UsoretiroController controlador = (UsoretiroController) session.getAttribute("usoretiro_list");
            controlador.reset(true);
        } catch (Exception e) {
            ensureAddErrorMessage(e, "Error al intentar refrescar la lista de Uso/Retiro");
        }
        //return listSetup();
        return "usoretiro_list";
    }

    /**
     * Función para preparar los detalles de una Devolución
     * @return String
     */
    public String detalleSetup() {
        return scalarSetup("devolucion_detalle");
    }

    /**
     * 
     * @return String
     */
    /*public String detailSetup() {
    return scalarSetup("devolucion_detail");
    }*/
    /**
     * Función para preparar la edición de una Devolución
     * @return String
     */
    public String editSetup() {
        return scalarSetup("devolucion_edit");
    }

    private String scalarSetup(String destination) {
        reset(false);
        devolucion = getDevolucionFromRequest();
        if (devolucion == null) {
            String requestDevolucionString = getRequestParameter("jsfcrud.currentDevolucion");
            addErrorMessage("The devolucion with id " + requestDevolucionString + " no longer exists.");
            String relatedControllerOutcome = relatedControllerOutcome();
            if (relatedControllerOutcome != null) {
                return relatedControllerOutcome;
            }
            return listarSetup();
        }
        return destination;
    }

    private Devolucion getDevolucionFromRequest() {
        String theId = getRequestParameter("jsfcrud.currentDevolucion");
        return (Devolucion) new DevolucionConverter().getAsObject(FacesContext.getCurrentInstance(), null, theId);
    }

    /**
     * Función para Obtener el ID-Uso/Retiro
     * @return
     */
    private Usoretiro getIdUsoFromRequest() {
        String theId = getRequestParameter("miiduso");
        return (Usoretiro) new UsoretiroConverter().getAsObject(FacesContext.getCurrentInstance(), null, theId);
    }

    private String getRequestParameter(String key) {
        return FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(key);
    }

    /**
     * Getter de todas las Devoluciones de la Base de Datos.(Wrapper)
     * @return List<Devolucion>
     */
    public List<Devolucion> getDevolucions() {
        //if (devolucions == null) {
        devolucions = getDevolucions(false);
        //}
        return devolucions;
    }

    /**
     * Getter de todas las Devoluciones directamente de la Base de Datos.
     * @param all
     * @return List<Devolucion>
     */
    public List<Devolucion> getDevolucions(boolean all) {
        EntityManager em = getEntityManager();
        try {
            Query q = em.createQuery("select object(o) from Devolucion as o where o.idUsoRetiro.idReserva.idestadoreserva=4");
            return q.getResultList();
        } finally {
            em.close();
        }
    }

    private void ensureAddErrorMessage(Exception ex, String defaultMsg) {
        String msg = ex.getLocalizedMessage();
        if (msg != null && msg.length() > 0) {
            addErrorMessage(msg);
        } else {
            addErrorMessage(defaultMsg);
        }
    }

    /**
     * Función para registrar mensajes de error.
     * @param msg
     */
    public static void addErrorMessage(String msg) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg);
        FacesContext.getCurrentInstance().addMessage(null, facesMsg);
    }

    /**
     * Función para registrar mensajes de Exito.
     * @param msg
     */
    public static void addSuccessMessage(String msg) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg);
        FacesContext.getCurrentInstance().addMessage("successInfo", facesMsg);
    }

    /**
     * Función para buscar una devolucion por su identificador (id)
     * @param id
     * @return Devolucion
     */
    public Devolucion findDevolucion(Integer id) {
        EntityManager em = getEntityManager();
        try {
            Devolucion o = (Devolucion) em.find(Devolucion.class, id);
            return o;
        } finally {
            em.close();
        }
    }

    /**
     * Función para generar la siguiente Devolución
     * @return int
     */
    private int getItemMax() {
        if (itemCount == -1) {
            EntityManager em = getEntityManager();
            try {
                itemCount = ((Long) em.createQuery("select count(o) from Devolucion as o").getSingleResult()).intValue();
                if (itemCount > 0) {
                    Devolucion d = (Devolucion) em.createQuery("SELECT s FROM Devolucion as s order by s.idDevolucion DESC").getResultList().get(0);
                    itemCount = d.getIdDevolucion();
                }
            } finally {
                em.close();
            }
        }
        return itemCount + 1;
    }

    /**
     * Función para contar la cantidad de devoluciones en la base de datos
     * @return itemCount
     */
    public int getItemCount() {
        if (itemCount == -1) {
            EntityManager em = getEntityManager();
            try {
                itemCount = ((Long) em.createQuery("select count(o) from Devolucion as o").getSingleResult()).intValue();
            } finally {
                em.close();
            }
        }
        return itemCount;
    }

    /*public int getFirstItem() {
    getItemCount();
    if (firstItem >= itemCount) {
    if (itemCount == 0) {
    firstItem = 0;
    } else {
    int zeroBasedItemCount = itemCount - 1;
    double pageDouble = zeroBasedItemCount / batchSize;
    int page = (int) Math.floor(pageDouble);
    firstItem = page * batchSize;
    }
    }
    return firstItem;
    }
    
    public int getLastItem() {
    getFirstItem();
    return firstItem + batchSize > itemCount ? itemCount : firstItem + batchSize;
    }
    
    public int getBatchSize() {
    return batchSize;
    }
    
    public String next() {
    reset(false);
    getFirstItem();
    if (firstItem + batchSize < itemCount) {
    firstItem += batchSize;
    }
    return "devolucion_list";
    }
    
    public String prev() {
    reset(false);
    getFirstItem();
    firstItem -= batchSize;
    if (firstItem < 0) {
    firstItem = 0;
    }
    return "devolucion_list";
    }*/
    private String relatedControllerOutcome() {
        String relatedControllerString = getRequestParameter("jsfcrud.relatedController");
        String relatedControllerTypeString = getRequestParameter("jsfcrud.relatedControllerType");
        if (relatedControllerString != null && relatedControllerTypeString != null) {
            FacesContext context = FacesContext.getCurrentInstance();
            Object relatedController = context.getApplication().getELResolver().getValue(context.getELContext(), null, relatedControllerString);
            try {
                Class<?> relatedControllerType = Class.forName(relatedControllerTypeString);
                Method detailSetupMethod = relatedControllerType.getMethod("detailSetup");
                return (String) detailSetupMethod.invoke(relatedController);
            } catch (ClassNotFoundException e) {
                throw new FacesException(e);
            } catch (NoSuchMethodException e) {
                throw new FacesException(e);
            } catch (IllegalAccessException e) {
                throw new FacesException(e);
            } catch (InvocationTargetException e) {
                throw new FacesException(e);
            }
        }
        return null;
    }

    private void reset(boolean resetFirstItem) {
        devolucion = null;
        devolucions = null;
        itemCount = -1;
    }
    private Map<Object, String> asString = null;

    /**
     * Función para Transformar la Devolución representado a una forma que pueda
     * ser transmitida en los jsps.
     * @return Map<Object, String>
     */
    public Map<Object, String> getAsString() {
        if (asString == null) {
            asString = new HashMap<Object, String>() {

                @Override
                public String get(Object key) {
                    if (key instanceof Object[]) {
                        Object[] keyAsArray = (Object[]) key;
                        if (keyAsArray.length == 0) {
                            return "(No Items)";
                        }
                        StringBuffer sb = new StringBuffer();
                        for (int i = 0; i < keyAsArray.length; i++) {
                            if (i > 0) {
                                sb.append("<br />");
                            }
                            sb.append(keyAsArray[i]);
                        }
                        return sb.toString();
                    }
                    return new DevolucionConverter().getAsString(FacesContext.getCurrentInstance(), null, (Devolucion) key);
                }
            };
        }
        return asString;
    }

    /**
     * Función que Vincula y Valida JSP con la Devolución Representado.
     * @param facesContext
     * @param component
     * @param value
     */
    public void validateCreate(FacesContext facesContext, UIComponent component, Object value) {
        DevolucionConverter converter = new DevolucionConverter();
        String newDevolucionString = converter.getAsString(FacesContext.getCurrentInstance(), null, new Devolucion());
        String devolucionString = converter.getAsString(FacesContext.getCurrentInstance(), null, devolucion);
        if (!newDevolucionString.equals(devolucionString)) {
            crearSetup();
        }
    }
}
